package com.zgf.easy.proxy;

import com.zgf.easy.proxy.anno.AutoModule;
import com.zgf.easy.proxy.anno.AutoService;
import com.zgf.easy.proxy.groovy.loader.GroovyLoader;
import com.zgf.easy.proxy.handler.BaseHandler;
import com.zgf.easy.proxy.handler.InvocationHandlerImpl;
import com.zgf.easy.proxy.mgr.ObjectMgr;
import com.zgf.easy.proxy.mgr.info.ImplInfo;
import com.zgf.easy.proxy.mgr.info.ModuleInfo;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.context.ApplicationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @project: easy_proxy
 * @author: zgf1
 * @date: 2021/12/26 13:13
 */
@Slf4j
public class EasyProxy {

    private final ClassLoader loader = EasyProxy.class.getClassLoader();
    private final Map<String, Object> objMap = new ConcurrentHashMap<>();

    private ApplicationContext applicationContext;
    private ThreadPoolExecutor executor;

    public EasyProxy() {
        this(null, null);
    }

    public EasyProxy(ApplicationContext applicationContext) {
        this(applicationContext, null);
    }

    public EasyProxy(ThreadPoolExecutor executor) {
        this(null, executor);
    }


    /**
     *
     * @param applicationContext 项目初始化透传到接口实现
     * @param executor 接口异步化调用线程池(和spring类似@Async，不配置同步调用)
     */
    public EasyProxy(ApplicationContext applicationContext, ThreadPoolExecutor executor) {
        this.executor = executor;
        this.applicationContext = applicationContext;
    }

    public final <T> T getProxy(String identity, Class<T> clazz) {
        if (clazz == null || identity == null || "".equals(identity)) {
            throw new IllegalArgumentException("Proxy.getProxy参数错误");
        }
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(clazz.getName() + "非接口类型");
        }
        StringBuilder keyer = new StringBuilder();
        keyer.append(identity).append("_")
                .append(clazz.getName());
        String key = keyer.toString();
        Object result = objMap.get(key);
        if (result != null) {
            return (T) result;
        }
        synchronized (objMap) {
            result = objMap.get(key);
            if (result != null) {
                return (T) result;
            }
            InvocationHandlerImpl handler = new InvocationHandlerImpl(executor);
            handler.setClazz(clazz);
            handler.setIdentity(identity);
            result = Proxy.newProxyInstance(loader, new Class[]{clazz}, handler);
            objMap.put(key, result);
        }
        return (T) result;
    }

    /**
     * groovy类
     * @param identity
     * @param content
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public void registerGroovyImpl(String identity, String content) throws IllegalAccessException, InstantiationException {
        Class<?> implCls = GroovyLoader.getClass(content);
        if (implCls == null) {
            throw new IllegalAccessException("groovyClass加载失败");
        }
        AutoService autoService = implCls.getAnnotation(AutoService.class);
        if (autoService == null) {
            throw new IllegalAccessException("groovyClass没有AutoService注解");
        }
        Object obj = implCls.newInstance();
        if (obj instanceof BaseHandler) {
            BaseHandler handler = (BaseHandler)obj;
            handler.setApplication(applicationContext);
            handler.init();
        }
        Class<?>[] interfaces = implCls.getInterfaces();
        if (interfaces == null || interfaces.length == 0) {
            throw new IllegalAccessException("groovyClass没有继承的接口");
        }
        ImplInfo implInfo = null;
        for (Class<?> interfaceClass : interfaces) {
            log.info("easy_proxy映射关系注册identity={}, interfaceClass={}, implCls={}", identity, interfaceClass.getName(), implCls.getName());
            implInfo = new ImplInfo();
            implInfo.setObj(obj);
            implInfo.setObjClass(implCls);
            implInfo.setAutoService(autoService);
            ObjectMgr.register(identity, interfaceClass, implInfo);
        }
    }

    /**
     * 动态身份注册
     * @param parentIdentity
     * @param identity
     */
    public void registerModuleInfo(String parentIdentity, String identity) {
        AutoModule autoModule = new AutoModule() {
            @Override
            public Class<? extends Annotation> annotationType() {
                return AutoModule.class;
            }

            @Override
            public String identity() {
                return identity;
            }

            @Override
            public String parentIdentity() {
                return parentIdentity;
            }

            @Override
            public String[] scanPackages() {
                return new String[0];
            }
        };
        ModuleInfo moduleInfo = new ModuleInfo();
        moduleInfo.setAutoModule(autoModule);
        ObjectMgr.register(autoModule.identity(), moduleInfo);
    }


    /**
     * 扫描身份下的实现类
     * @param bizModuleClsList
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public void scanPackages(Class<?>... bizModuleClsList) throws IllegalAccessException, InstantiationException {
        if (bizModuleClsList == null || bizModuleClsList.length == 0) {
            return;
        }
        String[] packages = null;
        AutoModule[] autoModules = null;
        Reflections reflections = null;
        Set<Class<?>> implClsSet = null;
        AutoService autoService = null;
        Object obj = null;
        BaseHandler handler = null;
        Class<?>[] interfaces = null;
        ImplInfo implInfo = null;
        ModuleInfo moduleInfo = null;
        for (Class<?> bizModuleCls : bizModuleClsList) {
            autoModules = bizModuleCls.getAnnotationsByType(AutoModule.class);
            if (autoModules == null || autoModules.length == 0) {
                continue;
            }
            for (AutoModule autoModule : autoModules) {
                moduleInfo = new ModuleInfo();
                moduleInfo.setAutoModule(autoModule);
                ObjectMgr.register(autoModule.identity(), moduleInfo);
                packages = autoModule.scanPackages();
                if (packages == null || packages.length == 0) {
                    packages = new String[]{bizModuleCls.getPackage().getName()};
                }
                log.info("easy_proxy扫描包路径{}", packages);
                reflections = new Reflections(packages);
                implClsSet = reflections.getTypesAnnotatedWith(AutoService.class);
                if (implClsSet == null || implClsSet.isEmpty()) {
                    continue;
                }
                for (Class<?> implCls : implClsSet) {
                    autoService = implCls.getAnnotation(AutoService.class);
                    if (autoService == null) {
                        continue;
                    }
                    //优先选择bean实例
                    if (applicationContext != null) {
                      obj = applicationContext.getBean(autoService.value(), implCls);
                    }
                    if(obj == null) {
                        obj = implCls.newInstance();
                    }
                    if (obj instanceof BaseHandler) {
                        handler = (BaseHandler) obj;
                        handler.setApplication(applicationContext);
                        handler.init();
                    }
                    interfaces = implCls.getInterfaces();
                    if (interfaces == null || interfaces.length == 0) {
                        continue;
                    }
                    for (Class<?> interfaceClass : interfaces) {
                        log.info("easy_proxy映射关系注册identity={}, interfaceClass={}, implCls={}", autoModule.identity(), interfaceClass.getName(), implCls.getName());
                        implInfo = new ImplInfo();
                        implInfo.setObj(obj);
                        implInfo.setObjClass(implCls);
                        implInfo.setAutoService(autoService);
                        ObjectMgr.register(autoModule.identity(), interfaceClass, implInfo);
                    }
                }
            }
        }
    }

}